Skip to content

Add ForwardSlashPath branded type and forward-slash path utilities#1853

Merged
pmcelhaney merged 4 commits intomainfrom
copilot/add-branded-string-utility-function
Apr 14, 2026
Merged

Add ForwardSlashPath branded type and forward-slash path utilities#1853
pmcelhaney merged 4 commits intomainfrom
copilot/add-branded-string-utility-function

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 14, 2026

Summary

Centralizes all replaceAll("\\", "/") path normalization behind a suite of utility functions that return a branded ForwardSlashPath type, enabling static enforcement that backslash-to-forward-slash conversion has occurred before a path is used in contexts that require it (chokidar watchers, ES module specifiers, URL routing).

// Before — scattered inline normalization
this.basePath = basePath.replaceAll("\\", "/");
const fullPath = nodePath.join(destination, path).replaceAll("\\", "/");

// After — centralized, typed, enforced
this.basePath = toForwardSlashPath(basePath);
const fullPath = pathJoin(destination, path); // returns ForwardSlashPath

New utility (src/util/forward-slash-path.ts):

  • ForwardSlashPath — branded type (string & { readonly [__forwardSlashPath]: never }) using a unique symbol to prevent accidental assignment of un-normalized strings
  • toForwardSlashPath(path: string): ForwardSlashPath — the core normalization function
  • pathJoin(...paths: string[]): ForwardSlashPath — wraps nodePath.join, returning a forward-slash path
  • pathRelative(from: string, to: string): ForwardSlashPath — wraps nodePath.relative, returning a forward-slash path
  • pathDirname(path: string): ForwardSlashPath — wraps nodePath.dirname, returning a forward-slash path
  • pathResolve(...paths: string[]): ForwardSlashPath — wraps nodePath.resolve, returning a forward-slash path

Updated call sites — 13 files replaced inline .replaceAll("\\", "/") and toForwardSlashPath(nodePath.xxx(...)) patterns with the new utilities:

  • src/server/file-discovery.ts, module-loader.ts, transpiler.ts
  • src/typescript-generator/repository.ts, script.ts, generate.ts, operation-coder.ts, operation-type-coder.ts, parameters-type-coder.ts, prune.ts
  • src/migrate/update-route-types.ts, src/app.ts, bin/counterfact.js

Several files (transpiler.ts, script.ts, operation-coder.ts, operation-type-coder.ts, parameters-type-coder.ts, file-discovery.ts, app.ts) no longer import nodePath at all.

Test updates:

  • test/server/transpiler.test.ts — removed local forwardSlash helper; now imports toForwardSlashPath
  • test/util/forward-slash-path.test.ts — 13 unit tests covering all exported functions; removed the pathResolve › returns an absolute path assertion which was platform-dependent (Windows prepends a drive letter, making the expected value technically incorrect)
Original Prompt

Create a utility function to replace replaceAll("\", "/") throughout the code.

Return a branded string and use that branded string wherever a path without backslashes is needed.

Manual acceptance tests

  • toForwardSlashPath("C:\\Users\\foo\\bar") returns "C:/Users/foo/bar" with no backslashes remaining
  • pathJoin("routes", "pets.ts") returns "routes/pets.ts" (forward slashes, cross-platform)
  • pathRelative("/a/b", "/a/b/c") returns "c" (forward slashes, cross-platform)
  • Server starts and handles requests normally — no regression in path-based routing
  • TypeScript compiler rejects assigning a plain string to a ForwardSlashPath-typed variable without going through one of the utility functions

Tasks

  • Created src/util/forward-slash-path.ts with ForwardSlashPath branded type, toForwardSlashPath, and four nodePath-wrapping helpers (pathJoin, pathRelative, pathDirname, pathResolve)
  • Replaced all inline .replaceAll("\\", "/") occurrences across 13 source files with toForwardSlashPath or the appropriate wrapper
  • Replaced all toForwardSlashPath(nodePath.xxx(...)) call-site patterns with the corresponding wrapper function
  • Updated test/server/transpiler.test.ts to import toForwardSlashPath instead of using a local helper
  • Added test/util/forward-slash-path.test.ts with 13 unit tests covering all exported functions
  • Removed platform-dependent pathResolve absolute-path test that failed on Windows (drive letter is prepended, making the expected value incorrect)
  • Added changeset (patch bump)

Copilot AI linked an issue Apr 14, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add branded string utility for replacing backslashes in paths Add ForwardSlashPath branded type and toForwardSlashPath utility Apr 14, 2026
Copilot AI requested a review from pmcelhaney April 14, 2026 02:04
Copy link
Copy Markdown
Collaborator

@pmcelhaney pmcelhaney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new function is mostly used to fix the output of nodePath.join() or nodePath.relative()

Create two new utility functions that wrap the nodePath functions so we can eliminate duplication.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 14, 2026

The new function is mostly used to fix the output of nodePath.join() or nodePath.relative()

Create two new utility functions that wrap the nodePath functions so we can eliminate duplication.

Done in commit 9faad6a. Added four wrapper functions to src/util/forward-slash-path.ts:

  • pathJoin(...paths) — wraps nodePath.join
  • pathRelative(from, to) — wraps nodePath.relative
  • pathDirname(path) — wraps nodePath.dirname
  • pathResolve(...paths) — wraps nodePath.resolve

All call sites updated; several files no longer import nodePath at all.

Copilot AI changed the title Add ForwardSlashPath branded type and toForwardSlashPath utility Add ForwardSlashPath branded type and forward-slash path utilities Apr 14, 2026
@pmcelhaney pmcelhaney marked this pull request as ready for review April 14, 2026 02:40
@pmcelhaney pmcelhaney enabled auto-merge April 14, 2026 02:40
Copilot AI requested a review from pmcelhaney April 14, 2026 02:40
Copy link
Copy Markdown
Collaborator

@pmcelhaney pmcelhaney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the unit test that's failing on Windows. It's not technically correct.

auto-merge was automatically disabled April 14, 2026 10:39

Head branch was pushed to by a user without write access

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 14, 2026

Remove the unit test that's failing on Windows. It's not technically correct.

Removed in commit a5e17e3. The pathResolve › returns an absolute path test expected /foo/bar but Windows prepends the drive letter (D:/foo/bar), so the assertion was platform-dependent and incorrect.

Copilot AI requested a review from pmcelhaney April 14, 2026 10:42
@pmcelhaney pmcelhaney enabled auto-merge April 14, 2026 10:44
@pmcelhaney pmcelhaney added this pull request to the merge queue Apr 14, 2026
Merged via the queue into main with commit 6a230a0 Apr 14, 2026
8 of 10 checks passed
@pmcelhaney pmcelhaney deleted the copilot/add-branded-string-utility-function branch April 14, 2026 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Branded string for forward slash paths

2 participants